package org.hrqing.chess.dao;

import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.hrqing.chess.entity.Log;
import org.hrqing.chess.util.ClassUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author Hrqing
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class LogDao {

    public static class LogRowMapper implements RowMapper<Log> {

        @Override
        public Log mapRow(ResultSet rs, int rowNum) {
            try {
                return ClassUtils.resultSet(rs, Log.class);
            } catch (Exception e) {
                log.error("LogRowMapper error", e);
            }
            return new Log();
        }
    }

    private final NamedParameterJdbcTemplate template;

    public List<Log> findCreatedTimeDesc() {
        String sql = """
                select id,
                name,
                color,
                executing,
                start,
                red,
                black,
                differ,
                depth,
                step,
                remark,
                created_time as createdTime
                from t_log
                order by created_time desc
                """;
        return template.query(sql, new LogRowMapper());
    }

    public Optional<Log> findTopCreatedTimeDesc() {
        String sql = """
                select id,
                name,
                color,
                executing,
                start,
                red,
                black,
                differ,
                depth,
                step,
                remark,
                created_time as createdTime
                from t_log
                order by created_time desc
                limit 1
                """;
        try {
            List<Log> results = template.query(sql, new LogRowMapper());
            return results.isEmpty() ? Optional.empty() : Optional.of(results.getFirst());
        }catch (EmptyResultDataAccessException e) {
            return Optional.empty();
        }
    }

    public Optional<Log> findById(String id) {
        String sql = """
                select id,
                name,
                color,
                executing,
                start,
                red,
                black,
                differ,
                depth,
                step,
                remark,
                created_time as createdTime
                from t_log where id = :id
                """;
        try {
            Log result = template.queryForObject(sql, Map.of("id", id), new LogRowMapper());
            return result.isNew() ? Optional.empty() : Optional.of(result);
        }catch (EmptyResultDataAccessException e) {
            return Optional.empty();
        }
    }

    public void save(Log log) {
        String selectSql = """
                select count(id) as count
                from t_log
                where id = :id
                """;
        String insertSql = """
                insert into t_log
                (id, name, color, executing, start, red, black, differ, depth, step, remark, created_time)
                values
                (:id, :name, :color, :executing, :start, :red, :black, :differ, :depth, :step, :remark, :createdTime)
                """;
        String updateSql = """
                update t_log
                set name = :name, color = :color, executing = :executing,
                start = :start, red = :red, black = :black, differ = :differ, depth = :depth, step = :step,
                remark = :remark, created_time = :createdTime
                where id = :id
                """;
        Long date = log.getCreatedTime() == null ? null : log.getCreatedTime().atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli();
        Map<String, Object> params = new HashMap<>();
        params.put("id", log.getId());
        params.put("name", log.getName());
        params.put("color", log.getColor() == null ? null : log.getColor().name());
        params.put("executing", log.getExecuting() == null ? null : log.getExecuting().name());
        params.put("start", log.getStart());
        params.put("red", log.getRed());
        params.put("black", log.getBlack());
        params.put("differ", log.getDiffer());
        params.put("depth", log.getDepth() == null ? null : log.getDepth().name());
        params.put("step", log.getStep());
        params.put("remark", log.getRemark());
        params.put("createdTime", date);
        boolean update = false;
        if (log.getId() != null) {
            Boolean exists = template.queryForObject(selectSql, Map.of("id", log.getId()), (rs, rowNum) -> {
                long count = rs.getLong("count");
                return count > 0L;
            });
            if (exists) {
                update = true;
            }
        }
        if (update) {
            template.update(updateSql, params);
        }else {
            template.update(insertSql, params);
        }
    }

    public void delete(String id) {
        if (id == null || id.trim().isEmpty()) {
            return;
        }
        String sql = """
                delete from t_log where id = :id
                """;
        template.update(sql, Map.of("id", id));
    }
}
